/*
 * @Author: czy
 * @Date: 2021-07-11 23:00:46
 * @LastEditTime: 2021-07-14 22:26:58
 */
#include "string.h"

#include "sys/system.h"
#include "sys/config.h"
#include "sys/type.h"

/**
 * @description: Alloc a block of memory. (First-fit version)
 * @param dpHandler    DP handle.
 * @param size  Size of memory block.
 * @return Handle if success, else 0.
 */
static unsigned int dPartitionAlloc(unsigned int dpHandler, unsigned int size)
{
    Partition *partition = (Partition *)dpHandler;
    Block *block = (Block *)(partition->head);
    int flag = 0;
    size = (((size + 4 - 1) / ALIGN) + 1) * ALIGN;
    while (block != 0)
    {
        if (block->size >= size)
        {
            flag = 1;
            break;
        }
        block = (Block *)block->next;
    }
    if (flag == 0)
    {
        return 0;
    }

    Block *tempBlock;
    if (block->size - size > ALIGN)
    {
        Block newBlock;
        newBlock.size = block->size - size;
        newBlock.next = block->next;
        newBlock.previous = block->previous;
        unsigned int address = (unsigned int)block + size;
        memcpy((void *)address, &newBlock, sizeof(Block));
        if (block->previous != 0)
        {
            tempBlock = (Block *)(block->previous);
            tempBlock->next = address;
        }
        else
        {
            partition->head = address;
        }
        if (block->next != 0)
        {
            tempBlock = (Block *)(block->next);
            tempBlock->previous = address;
        }
        unsigned int *blocksize = (unsigned int *)block;
        *blocksize = size;
        return (unsigned int)block + 4;
    }
    else
    {
        if (block->previous != 0)
        {
            tempBlock = (Block *)(block->previous);
            tempBlock->next = block->next;
        }
        else
        {
            partition->head = block->next;
        }
        if (block->next != 0)
        {
            tempBlock = (Block *)(block->next);
            tempBlock->previous = block->previous;
        }
        unsigned int *blocksize = (unsigned int *)block;
        *blocksize = block->size;
        return (unsigned int)block + 4;
    }
}

/**
 * @description: Free a block of memory. (First-fit version)
 * @param dpHandler DP handle.
 * @param start     Start address of memory block.
 * @return 1
 */
static unsigned int dPartitionFree(unsigned int dpHandler, unsigned int start)
{
    Partition *partition = (Partition *)dpHandler;
    if (start > (unsigned int)partition + partition->meta || start < (unsigned int)partition)
    {
        return 0;
    }
    Block *block = (Block *)(partition->head);
    Block *previousBlock, *nextBlock;
    Block newBlock;
    int flag = 0;
    unsigned int address = start - 4;

    newBlock.size = *((unsigned int *)address);
    if (newBlock.size < ALIGN)
    {
        return 0;
    }

    while (block != 0)
    {
        if ((unsigned int)block > start)
        {
            flag = 1;
            break;
        }
        block = (Block *)block->next;
    }
    if (flag == 1)
    {
        newBlock.previous = block->previous;
        newBlock.next = (unsigned int)block;
        previousBlock = (Block *)block->previous;
        if ((unsigned int)previousBlock != 0)
        {
            if ((unsigned int)previousBlock + previousBlock->size == address)
            {
                // Merge with previous memory block.
                address = (unsigned int)previousBlock;
                newBlock.size += previousBlock->size;
                newBlock.next = previousBlock->next;
                newBlock.previous = previousBlock->previous;
            }
            else
            {
                previousBlock->next = address;
            }
        }
        else
        {
            partition->head = address;
        }
        if (address + newBlock.size == (unsigned int)block)
        {
            // Merge with next memory block.
            newBlock.size += block->size;
            newBlock.next = block->next;
            nextBlock = (Block *)(block->next);
            if ((unsigned int)nextBlock != 0)
            {
                nextBlock->previous = block->previous;
            }
        }
        else
        {
            block->previous = address;
        }
    }
    else
    {
        if ((unsigned int)block + block->size == address)
        {
            address = (unsigned int)block;
            newBlock.size += block->size;
            newBlock.next = block->next;
            newBlock.previous = block->previous;
        }
        else
        {
            newBlock.next = 0;
            newBlock.previous = (unsigned int)block;
            block->next = address;
        }
    }
    memcpy((void *)address, &newBlock, sizeof(Block));
    return 1;
}

/**
 * @description: Alloc for a block of memory.
 * @param size  Memory block size.
 * @return Pointer to the first address of a memory block.
 */
void *malloc(unsigned int size)
{
    unsigned int UMEMHandler = (unsigned int)brk();
    return (void *)(dPartitionAlloc(UMEMHandler, size));
}

/**
 * @description: Free a block of memory.
 * @param ptr   Pointer to the first address of a memory block.
 */
void free(void *ptr)
{
    unsigned int UMEMHandler = (unsigned int)brk();
    dPartitionFree(UMEMHandler, (unsigned int)ptr);
}

/**
 * @description: Alloc for n block of memory and initialize to 0.
 * @param nitems    Number of blocks.
 * @param size      Memory block size.
 * @return Pointer to the first address of a memory block.
 */
void *calloc(int nitems, unsigned int size)
{
    unsigned int UMEMHandler = (unsigned int)brk();
    ;
    unsigned int totalSize = nitems * size;
    unsigned int handler = dPartitionAlloc(UMEMHandler, totalSize);
    memset((void *)handler, '\0', totalSize);
    return (void *)handler;
}

/**
 * @description: Realloc for a block of memory.
 * @param ptr   Pointer to the first address of old memory block.
 * @param size  Memory block size.
 * @return Pointer to the first address of new memory block.
 */
void *realloc(void *ptr, unsigned int size)
{
    unsigned int UMEMHandler = (unsigned int)brk();
    ;
    dPartitionFree(UMEMHandler, (unsigned int)ptr);
    return (void *)dPartitionAlloc(UMEMHandler, size);
}